package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.enums.CommentType;
import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import com.tanhua.dubbo.server.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author feihao
 * @version 1.0
 * @date 2021/7/1 19:46
 */

@Service(version = "1.0.0")
@Slf4j
public class QuanziApiImpl implements QuanZiApi {

    //评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "QUANZI_COMMENT_";

    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIEK_REDIS_KEY_PREFIX = "USER_LIKE_";

    //用户是否喜欢的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX = "USER_LOVE_";


    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdService idService;

    @Autowired
    private TimeLineService timeLineService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private VideoApi videoApi;

    /**
     * 查询动态
     *
     * @param userId
     * @param page
     * @param size
     * @return
     */
    @Override
    public PageInfo<Publish> queryPublishList(Long userId, Integer page, Integer size) {
        PageInfo<Publish> publishPageInfo = new PageInfo<>();
        publishPageInfo.setPageNum(page);
        publishPageInfo.setPageSize(size);
        //分析：查询好友的动态，实际上查询时间线表
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Order.desc("date")));
        Query query = new Query().with(pageable);
        List<TimeLine> timeLineList = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);

        if (timeLineList.isEmpty()) {
            //如果没有动态
            return publishPageInfo;
        }
        //获取时间线列表中的发布id的列表
        //hutool工具：
        List<Object> publishIdList = CollUtil.getFieldValues(timeLineList, "publishId");
                /*List<Object> publishIdList = new ArrayList<>();
                for (TimeLine timeLine : timeLineList) {
                    ObjectId publishId = timeLine.getPublishId();
                    publishIdList.add(publishId);
                }*/

        //根据动态id查询动态列表  （发布表的id,在每个时间线表的publishId保存）
        Query queryPublish = Query.query(Criteria.where("id").in(publishIdList)).with(Sort.by(Sort.Order.desc("created")));
        //查询的所有动态：publishs:
        List<Publish> publishes = mongoTemplate.find(queryPublish, Publish.class);
        if (publishes.isEmpty()) {
            return publishPageInfo;
        }
        publishPageInfo.setRecords(publishes);

        return publishPageInfo;
    }

    /**
     * 发布动态
     *
     * @param publish
     * @return
     */
    @Override
    public String savePublish(Publish publish) {
        //对publish对象校验
        if (ObjectUtils.isEmpty(publish.getUserId()) || ObjectUtils.isEmpty(publish.getText())) {
            //如果发布内容或userID为空， 发布失败，返回空，
            return null;
        }
        try {
            //设置主键id
            publish.setId(ObjectId.get());
            //设置自增长的pid    //自行维护一个自增长的Ip，通过redis实现自增长，
            publish.setPid(idService.createPid(IdType.PUBLISH));
            publish.setCreated(System.currentTimeMillis());

            //写入到publish表中

            System.out.println(publish);
            Publish save = mongoTemplate.save(publish, "quanzi_publish");
            System.out.println(save);


            //写入相册表   自己的动态表信息：

            Album album = new Album();
            album.setId(ObjectId.get());
            album.setCreated(System.currentTimeMillis());
            album.setPublishId(publish.getId());

            mongoTemplate.save(album, "quanzi_album_" + publish.getUserId());


            //写入好友的时间线表（异步写入）  写入 publishId,userId,createTime
            timeLineService.saveTimeLine(publish.getUserId(), publish.getId());

        } catch (Exception e) {
            //TODO　事务回滚：
            log.error("发布动态失败！" + e);
            e.printStackTrace();
        }
        return publish.getId().toHexString();

    }

    /**
     * 查询推荐动态
     *
     * @param userId
     * @param page
     * @param size
     * @return
     */

    @Override
    public PageInfo<Publish> queryRecommendPublishList(Long userId, Integer page, Integer size) {
        PageInfo<Publish> publishPageInfo = new PageInfo<>();
        publishPageInfo.setPageNum(page);
        publishPageInfo.setPageSize(size);

        // 查询推荐结果数据
        String redisKeyForPid = "QUANZI_PUBLISH_RECOMMEND_" + userId;
        String data = redisTemplate.opsForValue().get(redisKeyForPid);
        if (StringUtils.isEmpty(data)) {
            //如果没有推荐的pid，直接返回空
            return publishPageInfo;
        }

        //查询到的pid进行分页处理
        List<String> list = Arrays.asList(StringUtils.split(data, ","));

        //计算出开始和结束的分页如   ：0，10
        int[] intStartAndEnd = PageUtil.transToStartEnd(page - 1, size);
        int start = intStartAndEnd[0];   // 0

        // 10  或者list.size()的大小
        int end = Math.min(intStartAndEnd[1], list.size());
        //获取到所有的pid
        List<Long> pidList = new ArrayList<>();
        for (int i = start; i < end; i++) {
            pidList.add(Long.valueOf(list.get(i)));
        }

        if (pidList.size() == 0) {
            //没有则返回
            return publishPageInfo;
        }

        //根据pid查询publish （根据pid在publish表中查询发布的动态返回）
        Query query = Query.query(Criteria.where("pid").in(pidList)).with(Sort.by(Sort.Order.desc("created")));

        List<Publish> publishList = mongoTemplate.find(query, Publish.class);

        if (publishList.size() == 0) {
            return publishPageInfo;
        }

        //不为空，将信息设置到publishPageInfo返回
        publishPageInfo.setRecords(publishList);

        return publishPageInfo;
    }


    /**
     * 根基id查询动态
     *
     * @param id
     * @return
     */
    @Override
    public Publish queryPublishById(String id) {
        return mongoTemplate.findById(id, Publish.class);
    }

    /**
     * 点击点赞
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean likeComment(Long userId, String publishId) {
        //首先查询该用户是否已经点赞：
        if (queryUserIsLike(userId, publishId)) {
            return false;
        }

        //调用私有方法进行评论保存：
        Boolean resulte = saveComment(null, userId, publishId, CommentType.LIKE);

        if (!resulte) {
            //如果没有保存成功，返回false
            return false;
        }

        //保存成功，修改redis的点赞数量，修改用户的点赞状态：
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LIKE.toString();
        redisTemplate.opsForHash().increment(redisKey, hashKey, 1);

        //记录用户是否点赞：
        String userHashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().put(redisKey, userHashKey, "1");

        return true;
    }

    /**
     * 取消点赞
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLikeComment(Long userId, String publishId) {
        //首先查询该用户是否已经点赞：
        if (!queryUserIsLike(userId, publishId)) {
            return false;
        }

        //删除评论数据：
        Boolean result = deletecomment(userId, publishId, CommentType.LIKE);

        if (!result) {
            //取消点赞失败
            return false;
        }

        //修改redisKey的值：
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LIKE.toString();
        redisTemplate.opsForHash().increment(redisKey, hashKey, -1);

        //记录用户是否点赞：
        String userHashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().delete(redisKey, userHashKey);
        return true;
    }


    /**
     * 查询点赞数
     *
     * @param publishId
     * @return
     */
    @Override
    public Long queryLikeCount(String publishId) {
        //从Redis中命中查询，如果命中直接返回即可
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LIKE.toString();
        Object data = this.redisTemplate.opsForHash().get(redisKey, hashKey);
        if (ObjectUtil.isNotEmpty(data)) {
            //如果查到了，直接返回
            return Convert.toLong(data);
        }

        //没有则查询Mongodb
        Long count = queryCommentCount(publishId, CommentType.LIKE);
        //写入Redis中
        this.redisTemplate.opsForHash().put(redisKey, hashKey, String.valueOf(count));

        return count;
    }


    /**
     * 查看用户是否已经点赞。
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean queryUserIsLike(Long userId, String publishId) {
        //从redis中查询数据
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String userHashKey = COMMENT_USER_LIEK_REDIS_KEY_PREFIX + userId;
        Object data = this.redisTemplate.opsForHash().get(redisKey, userHashKey);
        if (ObjectUtil.isNotEmpty(data)) {
            return StrUtil.equals(Convert.toStr(data), "1");
        }

        //查询Mongodb，确定是否已经点赞
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("userId").is(userId)
                .and("commentType").is(CommentType.LIKE)
        );
        long count = this.mongoTemplate.count(query, Comment.class);
        if (count == 0) {
            return false;
        }

        //写入到redis中
        this.redisTemplate.opsForHash().put(redisKey, userHashKey, "1");

        return true;
    }

    /**
     * 喜欢该动态
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean loveComment(Long userId, String publishId) {
        //首先查询该用户是否已经喜欢：
        if (queryUserIsLove(userId, publishId)) {
            return false;
        }

        //调用私有方法进行评论保存：
        Boolean resulte = saveComment(null, userId, publishId, CommentType.LOVE);

        if (!resulte) {
            //如果没有保存成功，返回false
            return false;
        }

        //保存成功，修改redis的点赞数量，修改用户的点赞状态：
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LOVE.toString();
        redisTemplate.opsForHash().increment(redisKey, hashKey, 1);

        //记录用户是否点赞：
        String userHashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().put(redisKey, userHashKey, "1");

        return true;
    }

    /**
     * 取消喜欢该动态
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLoveComment(Long userId, String publishId) {
        //首先查询该用户是否已经喜欢：
        if (!queryUserIsLove(userId, publishId)) {
            return false;
        }

        //删除评论数据：
        Boolean result = deletecomment(userId, publishId, CommentType.LOVE);

        if (!result) {
            //取消喜欢失败
            return false;
        }

        //修改redisKey的值：
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LOVE.toString();
        redisTemplate.opsForHash().increment(redisKey, hashKey, -1);

        //记录用户是否点赞：
        String userHashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        redisTemplate.opsForHash().delete(redisKey, userHashKey);
        return true;
    }

    /**
     * 查询该动态的喜欢数
     *
     * @param publishId
     * @return
     */
    @Override
    public Long queryLoveCount(String publishId) {
        //从Redis中命中查询，如果命中直接返回即可
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.LOVE.toString();
        Object data = this.redisTemplate.opsForHash().get(redisKey, hashKey);
        if (ObjectUtil.isNotEmpty(data)) {
            //如果查到了，直接返回
            return Convert.toLong(data);
        }

        //没有则查询Mongodb
        Long count = queryCommentCount(publishId, CommentType.LOVE);
        //写入Redis中
        this.redisTemplate.opsForHash().put(redisKey, hashKey, String.valueOf(count));

        return count;
    }

    /**
     * 查询当前用户是否喜欢了这条动态
     *
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean queryUserIsLove(Long userId, String publishId) {
        //从redis中查询数据
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String userHashKey = COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
        Object data = this.redisTemplate.opsForHash().get(redisKey, userHashKey);
        if (ObjectUtil.isNotEmpty(data)) {
            return StrUtil.equals(Convert.toStr(data), "1");
        }

        //查询Mongodb，确定是否已经点赞
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("userId").is(userId)
                .and("commentType").is(CommentType.LOVE)
        );
        long count = this.mongoTemplate.count(query, Comment.class);
        if (count == 0) {
            return false;
        }

        //写入到redis中
        this.redisTemplate.opsForHash().put(redisKey, userHashKey, "1");

        return true;
    }

    /**
     * 查询评论列表
     *
     * @param publishId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {

        PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.asc("created")));
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.COMMENT.getType())).with(pageRequest);

        List<Comment> comments = this.mongoTemplate.find(query, Comment.class);

        PageInfo<Comment> pageInfoComment = new PageInfo<>();
        pageInfoComment.setPageNum(page);
        pageInfoComment.setPageSize(pageSize);
        pageInfoComment.setRecords(comments);

        return pageInfoComment;

    }

    /**
     * 发表评论：
     *
     * @param userId
     * @param publishId
     * @param content
     * @return
     */
    @Override
    public Boolean saveComment(Long userId, String publishId, String content) {
        Boolean aBoolean = saveComment(content, userId, publishId, CommentType.COMMENT);

        if (!aBoolean) {
            return false;
        }

        //修改redisKey的值： (发布成功)
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.COMMENT.toString();
        redisTemplate.opsForHash().increment(redisKey, hashKey, 1);
        return true;
    }


    /**
     * 查询评论数：
     *
     * @param publishId
     * @return
     */
    @Override
    public Long queryCommentCount(String publishId) {
        //从Redis中命中查询，如果命中直接返回即可
        String redisKey = COMMENT_REDIS_KEY_PREFIX + publishId;
        String hashKey = CommentType.COMMENT.toString();
        Object data = this.redisTemplate.opsForHash().get(redisKey, hashKey);
        if (ObjectUtil.isNotEmpty(data)) {
            //如果查到了，直接返回
            return Convert.toLong(data);
        }

        //没有则查询Mongodb
        Long count = queryCommentCount(publishId, CommentType.COMMENT);
        //写入Redis中
        this.redisTemplate.opsForHash().put(redisKey, hashKey, String.valueOf(count));

        return count;
    }


    /**
     * 保存评论的方法：
     *
     * @param content
     * @param userId
     * @param publishId
     * @param commentType
     * @return
     */
    private Boolean saveComment(String content, Long userId, String publishId, CommentType commentType) {
        try {
            Comment comment = new Comment();
            comment.setId(ObjectId.get());
            comment.setPublishId(new ObjectId(publishId));
            comment.setCommentType(commentType.getType());
            comment.setUserId(userId);
            comment.setCreated(System.currentTimeMillis());
            comment.setContent(content);

            Publish publish = queryPublishById(publishId);

            if (!ObjectUtils.isEmpty(publish)) {
                comment.setPublishUserId(publish.getUserId());
            } else {
                //查询评论：
                Comment comment1 = queryCommentById(publishId);
                if (ObjectUtil.isNotEmpty(comment1)) {
                    comment.setPublishUserId(comment1.getUserId());
                } else {
                    //小视频的评论
                    //查询小视频
                    Video video = videoApi.queryVideoById(publishId);
                    if (ObjectUtil.isNotEmpty(video)) {
                        comment.setPublishUserId(video.getUserId());
                    } else {
                        // 其他情况，直接返回
                        return false;
                    }
                }
            }

            mongoTemplate.save(comment);
            return true;
        } catch (Exception e) {
            log.error("保存评论失败~userId:" + userId, "publishId:" + publishId + "commentType:" + commentType + e);
        }
        return false;

    }

    /**
     * 根基publishId 查询评论：
     *
     * @param id
     * @return
     */
    private Comment queryCommentById(String id) {
        return this.mongoTemplate.findById(new ObjectId(id), Comment.class);
    }

    /**
     * 删除评论的方法（）写的评论不能删除
     *
     * @param userId
     * @param publishId
     * @param commentType
     * @return
     */
    private Boolean deletecomment(Long userId, String publishId, CommentType commentType) {
        Query query = Query.query(Criteria.where("userId").is(userId)
                .and("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType())
        );
        return this.mongoTemplate.remove(query, Comment.class).getDeletedCount() > 0;
    }


    /**
     * 查询评论数量：
     *
     * @param publishId
     * @param commentType
     * @return
     */
    private Long queryCommentCount(String publishId, CommentType commentType) {
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType())
        );
        return this.mongoTemplate.count(query, Comment.class);
    }


    /**
     * 查询评论表：喜欢、点赞、评论：
     *
     * @param userId
     * @param commentType
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentListByUser(Long userId, CommentType commentType, Integer page, Integer pageSize) {

        PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
        Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(commentType.getType())).with(pageRequest);
        List<Comment> comments = mongoTemplate.find(query, Comment.class);

        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setRecords(comments);
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        return pageInfo;
    }


    /**
     * 根据用户Id,查询本用户的所有动态
     *
     * @param userId
     * @return
     */
    @Override
    public PageInfo<Publish> queryUserPublish(Long userId, Integer page, Integer pageSize) {

        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //首相查询自己的相册表：
        Query query = new Query().with(Sort.by(Sort.Order.desc("created")));
        List<Album> albumList = mongoTemplate.find(query, Album.class, "quanzi_album_" + userId);
        //判断自己的相册表是否为空：空的直接返回pageInfo
        if (albumList.size() == 0) {
            return pageInfo;
        }
        //获取所有的发布id:publishId;
        List<Object> publishIdList = CollUtil.getFieldValues(albumList, "publishId");

        //根据publishId查询所有的publish:
        Query queryPublish = Query.query(Criteria.where("id").in(publishIdList)).with(Sort.by(Sort.Order.desc("created")));
        List<Publish> publishList = mongoTemplate.find(queryPublish, Publish.class);

        pageInfo.setRecords(publishList);

        return pageInfo;
    }
}
